package view;

import helper.StringUtils;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;

import model.CodeLine;
import model.Macro;
import model.MacroList;
import model.Variable;
import controller.Parser;

public class MacroEditor extends JDialog implements ActionListener {
	private static final long serialVersionUID = 1810175421243275968L;

	private final int PROGRAM_LINES = 100;
	private JTable table = new JTable();

	private static final String TITLE = "Create new macro";

	private JTextField name = new JTextField();
	private JTextField params = new JTextField();

	private JButton save = new JButton("Save");
	private JButton cancel = new JButton("Cancel");

	private MacroView macroView;

	public MacroEditor(MacroView macroView) {
		this.setLayout(new BorderLayout());
		this.setTitle(TITLE);
		this.add(makeInput(), BorderLayout.NORTH);
		this.add(makeTable(), BorderLayout.CENTER);
		this.add(makeButtons(), BorderLayout.SOUTH);
		this.pack();
		this.setVisible(true);
		this.macroView = macroView;
	}

	private JPanel makeInput() {
		JPanel panel = new JPanel();
		panel.setLayout(new GridLayout(/* 3 */0, 2, 6, 3));
		panel.add(new JLabel("Name"));
		panel.add(name);
		String tooltip = "Parameters have to be space sperated";
		JLabel label = new JLabel("Parameters");
		label.setToolTipText(tooltip);
		panel.add(label);
		params.setToolTipText(tooltip);
		panel.add(params);
		return panel;
	}

	private JComponent makeTable() {
		table.setModel(new CodeTable(PROGRAM_LINES));
		table.getColumnModel().getColumn(0).setPreferredWidth(20);
		table.getColumnModel().getColumn(1).setPreferredWidth(100);
		table.getColumnModel().getColumn(2).setPreferredWidth(300);
		JScrollPane pane = new JScrollPane(table);
		pane
				.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		pane.setAlignmentX(LEFT_ALIGNMENT);
		return pane;
	}

	private JPanel makeButtons() {
		JPanel panel = new JPanel();
		panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
		panel.add(cancel);
		panel.add(save);
		save.addActionListener(this);
		cancel.addActionListener(this);
		return panel;
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == save) {
			if (name.getText().trim().equals("")) {
				JOptionPane.showMessageDialog(this,
						"Please enter a name for the new macro",
						"Macro name is missing",
						JOptionPane.INFORMATION_MESSAGE);
			} else if (macroAlreadyExists()) {
				JOptionPane
						.showMessageDialog(
								this,
								"There is already a macro with this name. Please enter a different name",
								"Macro already exists",
								JOptionPane.INFORMATION_MESSAGE);
			} else if (!correctParamFormat()) {
				JOptionPane
						.showMessageDialog(
								this,
								"The parameters have a wrong format. Each Variable has to begin with a $ and they have got to be space seperated (ex. \"$A $B\")",
								"Wrong parameter format",
								JOptionPane.INFORMATION_MESSAGE);
			} else {
				saveMacro();
			}
		}
		if (e.getSource() == cancel) {
			this.setVisible(false);
		}
	}

	private boolean macroAlreadyExists() {
		for (Macro macro : MacroList.getInstance().getMacro()) {
			if (macro.getName().equals(name.getText().trim())) {
				return true;
			}
		}
		return false;
	}

	private boolean correctParamFormat() {
		if (params.getText().trim().equals("")) {
			return true;
		}
		String[] parameters = params.getText().trim().split(" ");
		boolean correct = true;
		for (String parameter : parameters) {
			System.out.println("Parameter: \"" + parameter + "\"");
			if (!parameter.startsWith("$")) {
				correct = false;
			}
			if (!StringUtils.isAlphaNumerical(parameter.substring(1))) {
				correct = false;
			}
		}
		return correct;
	}

	private Collection<Variable> parseVariable() {
		ArrayList<Variable> vars = new ArrayList<Variable>();
		if (!params.getText().trim().equals("") && correctParamFormat()) {
			for (String parameter : params.getText().trim().split(" ")) {
				vars.add(new Variable(parameter));
			}
		}
		return vars;
	}

	private void saveMacro() {
		Hashtable<Integer, String> code = new Hashtable<Integer, String>();
		Hashtable<Integer, String> labels = new Hashtable<Integer, String>();
		CodeTable model = (CodeTable) table.getModel();
		for (int i = 0; i < model.getRowCount(); i++) {
			if (model.getValueAt(i, model.getCodeColumn()) != null) {
				code.put(i, String.valueOf(model.getValueAt(i, model
						.getCodeColumn())));
			}
			if (model.getValueAt(i, model.getLabelColumn()) != null) {
				labels.put(i, String.valueOf(model.getValueAt(i, model
						.getLabelColumn())));
			}
		}
		HashMap<Integer, CodeLine> codeLine = Parser.getParser().parseText(
				code, labels);

		// fix serialization
		ArrayList<CodeLine> codeLines = new ArrayList<CodeLine>();
		codeLines.addAll(codeLine.values());

		Macro macro = new Macro();
		macro.setCodeLine(codeLines);
		macro.setName(name.getText().trim());
		macro.setVariable(parseVariable());
		MacroList.getInstance().addMacro(macro);
		MacroList.getInstance().store();

		macroView.refresh();

		this.setVisible(false);
	}
}
